/*
*  Impp - Instant Messenger
*
*  Copyright (C) 2008  Oliver Schneider
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "OscarServerCon.h"
#include "PackageUtils.h"
#include "SrvSnacPackage.h"
#include "events/PackageEvent.h"
#include "events/ConnectionEvent.h"



namespace Impp {

//*
BEGIN_EVENT_TABLE(OscarServerCon, wxEvtHandler)
	EVT_SOCKET(ID_SOCKET,OscarServerCon::onSocketEvent)
END_EVENT_TABLE()
//*/

OscarServerCon::OscarServerCon(Oscar* oscar) :
	oscar(oscar), client(NULL), requestId(0), readStatus(),connectionState(STATE_DISCONNECTED),
	packageQueue(new PackageQueue())
{
	Start(300,false);
}

OscarServerCon::~OscarServerCon()
{
	disconnect();
	delete packageQueue;
}

void OscarServerCon::setTaskManager(TaskManager* tasks){
	packageQueue->setTaskManager(tasks);
}

void OscarServerCon::onSocketEvent(wxSocketEvent & event){
	switch(event.GetSocketEvent()){
	case wxSOCKET_CONNECTION:
		{
			wxLogTrace(wxT("OSCAR_SERVER_CON"),wxT("OscarServerCon::onSocketEvent: wxSOCKET_CONNECTION"));
			connectionState=STATE_CONNECTED;
			ConnectionEvent event(this,CON_EVT_CONNECTED,EVT_CONNECTION,ID_OSCAR);
			oscar->AddPendingEvent(event);
		}
		break;
	case wxSOCKET_LOST:
		{
			wxLogTrace(wxT("OSCAR_SERVER_CON"),wxT("OscarServerCon::onSocketEvent: wxSOCKET_LOST"));
			disconnect();
			ConnectionEvent event(this,CON_EVT_LOST,EVT_CONNECTION,ID_OSCAR);
			oscar->AddPendingEvent(event);
			//std::cout << "Connection Lost" << std::endl;
		}
		break;
	case wxSOCKET_INPUT:
		{
			wxLogTrace(wxT("OSCAR_SERVER_CON"),wxT("OscarServerCon::onSocketEvent: wxSOCKET_INPUT"));
			//std::cout << "Data in"<< std::endl;
			if(Flap* flap = readPackage()){
				//std::cout << "Sending Event" << std::endl;
				PackageEvent event(this,flap,EVT_PACKAGE,ID_OSCAR);
				oscar->AddPendingEvent(event);
			}
		}
		break;
	case wxSOCKET_OUTPUT:
		wxLogTrace(wxT("OSCAR_SERVER_CON"),wxT("OscarServerCon::onSocketEvent: wxSOCKET_OUTPUT"));
		break;
	}

}


bool OscarServerCon::connect(wxString srv, int port){
	if(connectionState==STATE_DISCONNECTED){
		connectionState=STATE_CONNECTING;
		if(client==NULL){
			client = new wxSocketClient(wxSOCKET_WAITALL);
			client->SetEventHandler(*this,ID_SOCKET);
			client->SetNotify(wxSOCKET_INPUT_FLAG|wxSOCKET_OUTPUT_FLAG|wxSOCKET_CONNECTION_FLAG|wxSOCKET_LOST_FLAG);
			client->Notify(true);
			client->SetTimeout(5000);
		}
		wxIPV4address addr;
		addr.Hostname(srv);
		addr.Service(port);
		bool connected = client->Connect(addr,true);
		wxLogTrace(wxT("OSCAR_SERVER_CON"),wxT("OscarServerCon::connect: connected: %i,%i error: %i, ok: %i"),client->IsConnected(),connected, client->Error(),client->IsOk());
		wxLogTrace(wxT("OSCAR"),wxT("OscarServerCon::connect: waiting..."));

		if(!connected){
			disconnect();
			ConnectionEvent event(this,CON_EVT_LOST,EVT_CONNECTION,ID_OSCAR);
			oscar->AddPendingEvent(event);
		}else{
			connectionState=STATE_CONNECTED;
			ConnectionEvent event(this,CON_EVT_CONNECTED,EVT_CONNECTION,ID_OSCAR);
			oscar->AddPendingEvent(event);
		}
		return connected;
	}else{
		std::cout << "OscarServerCon is already connected or is connecting" << std::endl;
		return false;
	}
}

void OscarServerCon::disconnect(){
	wxLogTrace(wxT("OSCAR"),wxT("OscarServerCon::disconnect: disconnect called"));

	if(connectionState!=STATE_DISCONNECTED && connectionState!=STATE_DISCONNECTING){
		connectionState=STATE_DISCONNECTING;
		if(client){
			client->Destroy();
			wxDELETE(client);
			client=NULL;
		}
		readStatus.reset();

		packageQueue->deleteQueue();
		rates.clearRates();

		connectionState=STATE_DISCONNECTED;
	}else{
		wxLogDebug(wxT("OscarServerCon::disconnect: OscarServerCon is a not connected or is disconnecting"));
	}
}

bool OscarServerCon::isConnected(){
	return connectionState==STATE_CONNECTED;
}

bool OscarServerCon::read(void* data,size_t lng){

	client->Read(((byte*)data)+readStatus.offset,lng-readStatus.offset);
	readStatus.offset+=client->LastCount();

	if(readStatus.offset < lng){
		return false;
	}
	return true;
}

Flap* OscarServerCon::readPackage(){
	if(connectionState==STATE_CONNECTED){
		if(readStatus.status==READSTATUS_HEADER){
			//std::cout << "READSTATUS_HEADER" << std::endl;
			if(!readStatus.header){
				// header
				readStatus.header = new Header();
				readStatus.dataLength=6;
				//std::cout << "Hader erstellt" << std::endl;
			}
			//std::cout << "reading Header" << std::endl;
			if(read(readStatus.header,6)){
				//std::cout << "Header read" << std::endl;
				if(readStatus.header->id!= '*'){
					std::cout << "OscarServerCon::readPackage: Bad Header, no '*' at the beginning" << std::endl;
					toHex(std::cout,(byte*)readStatus.header,6);

					readStatus.dataLength=0;
					delete readStatus.header;
					readStatus.header=NULL;

					return NULL;
				}
				readStatus.dataLength = swapBytes(readStatus.header->sizeBE);
				readStatus.offset=0;
				readStatus.status=READSTATUS_DATA;
				return NULL;
			}

		}else if(readStatus.status==READSTATUS_DATA){
			//std::cout << "READSTATUS_DATA" << std::endl;
			if(!readStatus.data){
				readStatus.data = new byte[readStatus.dataLength];
				readStatus.offset=0;
				//std::cout << "data erstellt" << std::endl;
			}
			//std::cout << "reading data" << std::endl;
			if(read(readStatus.data,readStatus.dataLength)){
				//std::cout << "data Read" << std::endl;
				Flap* flap = new Flap(readStatus.header,readStatus.data,readStatus.dataLength);
				//std::cout << "Package read"<< std::endl;
				readStatus.reset();
				return flap;
			}else{
				return NULL;
			}
		}else{
			std::cout << "OscarServerCon::readPackage: ERROR: Invalid readStatus Status" << std::endl;
			return NULL;
		}
	}else{
		wxLogDebug(wxT("OscarServerCon::readPackage: not connected!"));
		return NULL;
	}
	return NULL;
}


bool OscarServerCon::sendPackage(Chan chan, CliPackage* pack, const Priority & priority,const TaskHandle & handleType, const qword & handle){
	if(connectionState==STATE_CONNECTED){
		if(chan == DATA){
			packageQueue->addToQueue(pack, priority, handleType, handle);
			return true;
		}else{
			return doSendPackage(chan,pack);
		}
	}else{
		std::cout << "OscarServerCon::sendPackage: Server is not connected" << std::endl;
		return false;
	}
}

bool OscarServerCon::doSendPackage(Chan chan, CliPackage* pack){
	if(connectionState==STATE_CONNECTED){
		size_t size =pack->size();

		Header header;

		header.id='*';
		header.channel=chan;
		header.sequenNrBE=swapBytes(nextSeqNumber());
		header.sizeBE=swapBytes(size);

		client->Write(&header,6);
		client->Write(pack->getData(),size);

		delete pack;
		return true;
	}else{
		std::cout << "OscarServerCon::doSendPackage: Server is not connected" << std::endl;
		return false;
	}

}

word OscarServerCon::nextSeqNumber(){
	return seqNumber++;
}

dword OscarServerCon::nextRequestId(){
	return requestId++;
}

State OscarServerCon::getConnectionState(){
	return connectionState;
}

void OscarServerCon::Notify(){
	QueuePackage qp = packageQueue->getNextPackageToSend();
	if(qp.pack!=NULL){
		doSendPackage(DATA,qp.pack);
		rates.calculateNextLevel(qp.rate,true);

	}
}

void OscarServerCon::setRates(std::vector<Rate> & rates, std::vector<RateGroup> & rateGroups){
	this->rates.setRates(rates,rateGroups);
	this->packageQueue->setRateManagement(&(this->rates));
}

}
